package testApplets;

import java.awt.geom.Line2D;
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Vector;



import controlP5.ControlP5;
import controlP5.RadioButton;

import marchingCubes.DualCube;
import marchingCubes.DualCubeEngine;
import marchingCubes.DualEdge;
import marchingCubes.IsMCVertex;
import marchingCubes.MCube;
import marchingCubes.MCubeEngine;
import marchingCubes.MCubeEngineLowMemory;
import meshToSolid.ContainerGrid;
import meshToSolid.STLWriter;
import meshToSolid.ContainerGrid.Container;

import peasy.PeasyCam;
import processing.core.PApplet;
import processing.core.PGraphics3D;
import processing.core.PMatrix3D;
import processing.core.PVector;

import simpleGeom.BasicMesh;
import simpleGeom.Bounds3D;
import simpleGeom.Box3D;
import simpleGeom.DepthAnalyser3D;
import simpleGeom.Face3D;
import simpleGeom.HasBounds3D;
import simpleGeom.HasPos3D;
import simpleGeom.Line3D;
import simpleGeom.Mesh;
import simpleGeom.SimpleTriangle;

import simpleGeom.Node;
import simpleGeom.SimpleObjLoaderStream;
import simpleGeom.Triangle3DUVN;
import simpleGeom.TrianglePreCalc;
import simpleGeom.Vec3D;

public class DualMeshToSolid extends PApplet {
	
	Bounds3D b3d = new Bounds3D();
	Bounds3D realBounds = new Bounds3D();
	BasicMesh mesh=new BasicMesh();
	DualCube[][][] grid;
	ContainerGrid cGrid;
	int cGridRes = 20;
	ArrayList<Line3D> lines;
	float sX;
	float sY;
	float sZ;
	int nX;
	int nY;
	int nZ;
	int nYZ;
	float dimHigh = 0.002f;// 0.0034//0.003//0.007
	float dim = 0.03f;
	float goalThick=0.03f;
	float c=(goalThick-dimHigh*0.9f)/dimHigh*0.9f;
	float dimFac=0.15f;//thickness inside
	float dimLow = 0.008f;//0.1
	float dimHull = dim * 0.5f;// 0.003//0.007
	float iso = 0.01f;// 0.005//0.006

	String folderPath = "/Users/caad/Desktop/Archive/";
	
	String pathObjIn = folderPath + "testPiece6.obj";// form |
																	// 11-09-12
																	// |
																	// 14'28'40
																	// |.obj
	String pathDistOut = pathObjIn+".txt";
	Vector<SimpleTriangle> meFaces;
	boolean flagMarching = false;
	boolean flagDistance = false;

	ControlP5 cp5;
	
	public float minX = 0.6f;// 26
	public float maxX = 0;// 5
	public float minY = 0.16f;// 20
	public float maxY = 0.16f;// 72
	public float minZ = 0.3f;// 28
	public float maxZ = 0.3f;// 28

	String pathObjOut = "";
	Box3D innerVolume;
	Bounds3D meshBounds;
	PeasyCam cam;
	boolean isPreview = true;
	boolean flagResetPreview;
	boolean flagResetFine;
	boolean flagRefresh;
	boolean flagSave;
	float rotY = 0;
	Vector<SimpleTriangle> borderFaces = new Vector<SimpleTriangle>();
	
	float goalX=1.7f;
	float goalY=3.5f;
	float goalZ=1.7f;


	public void setup() {
		size(1300, 750, P3D);
		//
		b3d = new Bounds3D();
		realBounds = new Bounds3D();
		/*realBounds.addPoint(new Vec3D(1.5f, 1.5f, 1.5f));
		realBounds.addPoint(new Vec3D(0, 0, 0));*/
		loadData();
		setBounds();
		
		//calculateDistFine();
		println("exportBds: "+b3d);
		//saveDistance(pathDistOut);
		
		resetPreview();
		findBorders();
		createGUI();
		//resetFine();
		cam=new PeasyCam(this,400);
	}

	public void createGUI() {
			cp5 = new ControlP5(this);
		/*	cp5.addSlider("minX").setPosition(20, 20).setRange(0, 100)
					.setSize(200, 20);
			;
			cp5.addSlider("maxX").setPosition(20, 50).setRange(0, 100)
					.setSize(200, 20);
			;
			cp5.addSlider("minY").setPosition(20, 80).setRange(0, 100)
					.setSize(200, 20);
			;
			cp5.addSlider("maxY").setPosition(20, 110).setRange(0, 100)
					.setSize(200, 20);
			;
			cp5.addSlider("minZ").setPosition(20, 140).setRange(0, 100)
					.setSize(200, 20);
			;
			cp5.addSlider("maxZ").setPosition(20, 170).setRange(0, 100)
			.setSize(200, 20);
	;*/
			//cp5.setAutoDraw(false);
	
			RadioButton r = cp5.addRadioButton("radioButton").setPosition(20, 200)
					.setSize(10, 10).setColorForeground(color(120))
					.setColorActive(color(255)).setColorLabel(color(255))
					.setItemsPerRow(8).setSpacingColumn(20).addItem("50", 1)
					.addItem("100", 2)
					.addItem("150", 3)
					.addItem("200", 4)
					.addItem("250", 5)
					.addItem("300", 6)
					.addItem("350", 7)
					.addItem("400", 8)
	
			;
		}

	void gui() {
		//noLights();
		stroke(0);
		fill(0);
	
		
		
		PGraphics3D p3d = ((PGraphics3D) g);
		PMatrix3D currCameraMatrix = new PMatrix3D(p3d.camera);
	
		camera();
		//rect(0, 0, 200, 800);
		//cp5.draw();
		p3d.camera = currCameraMatrix;
		
	
	}

	public void radioButton(int a) {
		flagRefresh = true;
		rotY = HALF_PI * a*0.5f;
		// println("a radio Button event: "+a);
	}

	public void resetPreview() {
		dim = dimLow;
		iso = dim * 0.90f;
		dimHull = iso/dim;
		setBounds();
		createGrid();
		calculateDistancesSimple();
		
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		println("start marchincube");
		dualCube();
		//meFaces = MCubeEngineLowMemory.initCubesTest(grid, iso, null);
		findBorders();
	}
	public void calculateDistFine(){
		meFaces = null;
		dim = dimHigh;
		iso = dim * 0.90f;
		dimHull = dim * 0.5f;
		setBounds();
		createGrid();
		calculateDistances();
		
	}
	DualCubeEngine dc;
	public void intersect(){
		 dc=new DualCubeEngine();
		dc.init(mesh.getBounds3D(),dim*2f);
		for (int i = 0; i < mesh.faces.size(); i++) {
			if (i % 100000 == 0)
				println(i);
			SimpleTriangle sT = (SimpleTriangle)mesh.faces.get(i);
			dc.scanTriangle(sT.getNode(0), sT.getNode(1), sT.getNode(2));
		}
		setBorders(dc);
		boolean[][][] reached = new boolean[dc.nX][dc.nY][dc.nZ];
		reached[0][(int)(dc.nY *0.5f)][(int)(dc.nZ *0.5f)] = true;
		dc.calculateDepth(reached);
		
		meFaces=dc.getFaces();
		dualMesh=new BasicMesh();
		dualMesh.load(meFaces);
		//dualMesh.scale(dim);
		println("faces: "+meFaces.size());
		//dualMesh.translate(sX, sY, sZ);
		println("dualMesh:"+dualMesh.faces.size());
	}
	public void setBorders(DualCubeEngine dc) {
		float oZ = 0.2f;
		int nOZ = (int) (oZ / dim);
		float oY = 0.2f;
		int nOY = (int) (oY / dim);

		boolean flagSave;
		boolean drawBox;

		int z1=nOZ;
		int z2=dc.nZ-nOZ-1;
		
		int y1=nOY;
		int y2=dc.nY-nOY-1;
		Vector<DualEdge> dualEdges = new Vector<DualEdge>();
		for (int x = 0; x < dc.nX; x++) {
			for (int y = 0; y < dc.nY; y++) {
				DualEdge dC1 = dc.addEdge(x, y, z1, x, y, z1 + 1);
				dC1.intersection = new Vec3D(dc.getXFloat(x), dc.getYFloat(y),
						dc.getZFloat(nOZ));
				dC1.normal = new Vec3D(0, 0., 1);
				DualEdge dC2 = dc.addEdge(x, y, z2, x, y, z2-1);
				dC2.intersection = new Vec3D(dc.getXFloat(x), dc.getYFloat(y),
						dc.getZFloat(z2-1));
				dC2.normal = new Vec3D(0, 0, 1);
			}
		}
		
		for (int x = 0; x < dc.nX; x++) {
			for (int z = 0; z < dc.nZ; z++) {
				DualEdge dC1 = dc.addEdge(x, y1, z, x, y1 + 1, z);
				dC1.intersection = new Vec3D(dc.getXFloat(x),
						dc.getYFloat(nOY), dc.getZFloat(z));
				dC1.normal = new Vec3D(0, 1, 0);
				DualEdge dC2 = dc.addEdge(x, y2, z, x,
						y2-1, z);
				dC2.intersection = new Vec3D(dc.getXFloat(x),
						dc.getYFloat(y2-1), dc.getZFloat(z));
				dC2.normal = new Vec3D(0, 1, 0);
			}
		}
	}
	public void resetFine() {
		meFaces = null;
		dim = dimHigh;
		iso = dim * 0.90f;
		
		setBounds();
		createGrid();
		calculateDistances();
		findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		dualCube();
		
		//intersect();
	}

	public void saveSTL(String file) {
		STLWriter writer = new STLWriter();
		writer.beginSave(file + ".stl", meFaces.size());
		for (int i = 0; i < meFaces.size(); i++) {
			SimpleTriangle f = meFaces.get(i);
			writer.face(f.getNode(0), f.getNode(1), f.getNode(2));
		}
		writer.endSave();
	}

	public void findBorders() {
		borderFaces = new Vector<SimpleTriangle>();
		for (int i = 0; i < mesh.faces.size(); i++) {
			SimpleTriangle tri = (SimpleTriangle)mesh.faces.get(i);
			Box3D box = tri.getBounds3D();
			if (box.getX1() < b3d.getX1() && box.getX2() > b3d.getX1())
				borderFaces.add(tri);

			if (box.getX1() < b3d.getX2() && box.getX2() > b3d.getX2()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY1() && box.getY2() > b3d.getY1()) {
				borderFaces.add(tri);
			}
			if (box.getY1() < b3d.getY2() && box.getY2() > b3d.getY2()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ1() && box.getZ2() > b3d.getZ1()) {
				borderFaces.add(tri);
			}
			if (box.getZ1() < b3d.getZ2() && box.getZ2() > b3d.getZ2()) {
				borderFaces.add(tri);
			}
		}
	}

	BasicMesh dualMesh;
	public void dualCube() {
		//findOutline(0, (int) (nY * 0.5f), (int) (nZ * 0.5f));
		
		DualCubeEngine dc=new DualCubeEngine();
		meFaces=dc.getFaces(grid, iso);
		dualMesh=new BasicMesh();
		dualMesh.load(meFaces);
		dualMesh.scale(dim);
		println("faces: "+meFaces.size());
		dualMesh.translate(sX, sY, sZ);
		println("dualMesh:"+dualMesh.faces.size());
		// this.saveObjPrintwriter(pathObjOut);
	}

	

	public void calculateDistancesSimple() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);

		for (int i = 0; i < mesh.faces.size(); i++) {
			if (i % 1000000 == 0) {
				println(i + "/" + mesh.faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) mesh.faces.get(i);

			Box3D faceBounds = f.getBounds3D();
			// faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 1;
			int y2 = yK(faceBounds.y2) + 1;
			int z2 = zK(faceBounds.z2) + 1;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			HasPos3D v1 = f.getNode(0);
			HasPos3D v2 = f.getNode(1);
			HasPos3D v3 = f.getNode(2);
			Vec3D v12 = Vec3D.sub(v2, v1);
			Vec3D v13 = Vec3D.sub(v3, v1);
			Vec3D n = Vec3D.cross(v12, v13);
			n.normalize();
			float d0 = -n.dot(f.getNode(0));
			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						float planeDist = n.dot(x * dim + sX, y * dim + sY, z
								* dim + sZ)
								+ d0;
						if (planeDist < iso) {
							DualCube cell = grid[x][y][z];
							cell.setValue(0);
						}
					}
				}
			}
		}
	}

	public void calculateDistances() {

		// Box3D box3D = b3d.getOffsetBox(dim*4f+iso);

		for (int i = 0; i < mesh.faces.size(); i++) {
			if (i % 100000 == 0) {
				println(i + "/" + mesh.faces.size());
			}
			SimpleTriangle f = (SimpleTriangle) mesh.faces.get(i);

			Box3D faceBounds = f.getBounds3D();
			faceBounds = faceBounds.getOffsetBox(iso);
			int x1 = xK(faceBounds.x1) - 1;
			int y1 = yK(faceBounds.y1) - 1;
			int z1 = zK(faceBounds.z1) - 1;
			x1 = max(x1, 0);
			y1 = max(y1, 0);
			z1 = max(z1, 0);
			int x2 = xK(faceBounds.x2) + 1;
			int y2 = yK(faceBounds.y2) + 1;
			int z2 = zK(faceBounds.z2) + 1;
			x2 = min(x2, nX);
			y2 = min(y2, nY);
			z2 = min(z2, nZ);

			PMatrix3D m = new PMatrix3D();
			Vec3D planeAxis = new Vec3D(0, 0, 1);
			Line3D l1;
			Line3D l2;
			Line3D l3;

			HasPos3D v1 = f.getNode(0);
			HasPos3D v2 = f.getNode(1);
			HasPos3D v3 = f.getNode(2);
			Vec3D v12 = Vec3D.sub(v2, v1);
			Vec3D v13 = Vec3D.sub(v3, v1);
			Vec3D n = Vec3D.cross(v12, v13);
			n.normalize();
			float angle = (float) Math.acos(n.dot(planeAxis));

			Vec3D u = n.cross(planeAxis);
			u.normalize();

			float c = (float) Math.cos(angle);
			float s = (float) Math.sin(angle);
			float t = 1f - c;

			m.m00 = c + u.getX() * u.getX() * t;
			m.m01 = u.getX() * u.getY() * t - u.getZ() * s;
			m.m02 = u.getX() * u.getZ() * t + u.getY() * s;

			m.m10 = u.getY() * u.getX() * t + u.getZ() * s;
			m.m11 = c + u.getY() * u.getY() * t;
			m.m12 = u.getY() * u.getZ() * t - u.getX() * s;

			m.m20 = u.getZ() * u.getX() * t - u.getY() * s;
			m.m21 = u.getZ() * u.getY() * t + u.getX() * s;
			m.m22 = c + u.getZ() * u.getZ() * t;

			float[] f1 = new float[] { v1.getX(), v1.getY(), v1.getZ() };
			float[] f2 = new float[] { v2.getX(), v2.getY(), v2.getZ() };
			float[] f3 = new float[] { v3.getX(), v3.getY(), v3.getZ() };

			float[] f1T = m.mult(f1, null);
			float[] f2T = m.mult(f2, null);
			float[] f3T = m.mult(f3, null);

			l1 = new Line3D(f1T[0], f1T[1], f1T[2], f2T[0], f2T[1], f2T[2]);
			l2 = new Line3D(f2T[0], f2T[1], f2T[2], f3T[0], f3T[1], f3T[2]);
			l3 = new Line3D(f3T[0], f3T[1], f3T[2], f1T[0], f1T[1], f1T[2]);

			float d0 = -n.dot(f.getNode(0));

			for (int x = x1; x < x2; x++) {
				for (int y = y1; y < y2; y++) {
					for (int z = z1; z < z2; z++) {
						// float minD = Float.MAX_VALUE;
						DualCube cell = grid[x][y][z];
						float cellX = getX(x);
						float cellY = getY(y);
						float cellZ = getZ(z);
						if (faceBounds.isOverlap(cellX - dim, cellY - dim,
								cellZ - dim, cellX + dim, cellY + dim, cellZ
										+ dim)) {
							float planeDist = n.dot(cellX, cellY, cellZ) + d0;

							if (planeDist < cell.getValue()) {
								float cDist = Float.MAX_VALUE;
								float[] pt = new float[] { cellX, cellY, cellZ };
								float[] trans = new float[3];
								trans = m.mult(pt, null);
								float cX = trans[0];
								float cY = trans[1];
								float cZ = trans[2];
								int r1 = Line2D.relativeCCW(l1.x1, l1.y1,
										l1.x2, l1.y2, cX, cY);
								int r2 = Line2D.relativeCCW(l2.x1, l2.y1,
										l2.x2, l2.y2, cX, cY);
								int r3 = Line2D.relativeCCW(l3.x1, l3.y1,
										l3.x2, l3.y2, cX, cY);
								float cDistance;
								boolean isPlane=false;
								if (r1 == r2 && r1 == r3) {
									isPlane=true;
									cDistance = (cZ - l1.z1) * (cZ - l1.z1);
									if (cDistance < cDist) {
										
										cDist = cDistance;
									}
								} else {
									isPlane=false;
									float d1 = (float) l1.ptSegDistSq(cX, cY,
											cZ);
									float d2 = (float) l2.ptSegDistSq(cX, cY,
											cZ);
									float d3 = (float) l3.ptSegDistSq(cX, cY,
											cZ);
									if (d1 < cDist) {
										cDist = d1;
									}
									if (d2 < cDist) {
										cDist = d2;
									}
									if (d3 < cDist) {
										cDist = d3;
									}
								}
								cDist = sqrt(cDist);
								if (cDist < cell.getValue()) {
									cell.setValue(cDist);
									
									cell.normal=n;
									
								}
							}
						}
					}
				}
			}
		}
	}

	public void createGrid() {
		nX = (int) (b3d.getDimX() / dim);
		nY = (int) (b3d.getDimY() / dim);
		nZ = (int) (b3d.getDimZ() / dim);
		nYZ = nY * nZ;

		sX = b3d.getX1();
		sY = b3d.getY1();
		sZ = b3d.getZ1();
		grid = new DualCube[nX][nY][nZ];
		println(nX + " " + nY + " " + nZ);
		int id = 0;
		for (int x = 0; x < nX; x++) {
			println("x: " + x + "/" + nX);
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					
					DualCube cell = new DualCube((short)x, (short)y,(short) z);
					
					id++;
					cell.setValue(1000);
					grid[x][y][z] = cell;
				}
			}
		}
	}

	public void loadData() {
		mesh.load(pathObjIn);
	}

	public void loadDistance(String file) {
		String[] lines = this.loadStrings(file);
		String cString = lines[0];
		String[] strings = cString.split(" ");
		int nX = Integer.valueOf(strings[0]);
		println("nX: " + nX);
		int nY = Integer.valueOf(strings[1]);
		int nZ = Integer.valueOf(strings[2]);

		grid = new DualCube[nX][nY][nZ];
		println(nY);
		int j = 0;
		for (int i = 1; i < lines.length; i++) {
			String l = lines[i];
			j = i - 1;
			int cX = ((int) (j * 1f / (nZ * nY)));
			int cY = (int) (j * 1f / nZ) % nY;
			int cZ = j % (nZ);
			double d = Double.valueOf(l);
			grid[cX][cY][cZ].setValue((float) d);
		}
	}

	public void saveDistance(String file) {
		try {
			FileOutputStream fileOutputStream = new FileOutputStream(file);
			PrintStream printstream = new PrintStream(fileOutputStream);
			printstream.println(grid.length+" "+grid[0].length+" "+grid[0][0].length);
			printstream.println(realBounds.x1+" "+realBounds.y1+" "+realBounds.z1+" "+realBounds.x2+" "+realBounds.y2+" "+realBounds.z2);
			for (int x = 0; x < grid.length; x++) {
				println("x"+x+"/"+grid.length);
				for (int y = 0; y < grid[x].length; y++) {
					for (int z = 0; z < grid[x][y].length; z++) {
						DualCube c = grid[x][y][z];
						printstream.println("" + c.getValue());
					}
				}
			}
			printstream.close();
			fileOutputStream.close();
		} catch (Exception e) {
			System.err.println("error writing file");
		}
	}

	public void saveObjPrintwriter(BasicMesh mesh,String file) {
		mesh.saveObj(file);
		
	}

	public void findOutline() {

		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				boolean flag = false;
				for (int x = nX - 1; x >= 0; x--) {
					DualCube nb = grid[x][y][z];

					if (nb.getValue() > iso) {
						nb.setValue(0);
					} else {
						flag = true;
						x = -1;
					}
				}
				if (!flag) {
					for (int x = nX - 1; x >= 0; x--) {
						DualCube nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
		for (int y = 0; y < nY; y++) {
			for (int z = 0; z < nZ; z++) {
				DualCube nb = grid[nX - 1][y][z];
				nb.setValue(1000);
			}
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					if (y < 20 || y > nY - 15) {
						DualCube nb = grid[x][y][z];
						nb.setValue(1000);
					}
				}
			}
		}
	}

	public void findOutline(int cX, int cY, int cZ) {
		Vector<DualCube> candidates = new Vector<DualCube>();

		/*
		 * for (int x=0;x<nX;x++){ for (int y=0;y<nY;y++){
		 * candidates.add(grid[x][y][0]); candidates.add(grid[x][y][nZ-1]); } }
		 * for (int x=0;x<nX;x++){ for (int z=0;z<nZ;z++){
		 * candidates.add(grid[x][0][z]); candidates.add(grid[x][nY-1][z]); } }
		 * for (int y=0;y<nY;y++){ for (int z=0;z<nZ;z++){
		 * candidates.add(grid[0][y][z]); candidates.add(grid[nX-1][y][z]); } }
		 */
		candidates.add(grid[cX][cY][cZ]);
		for (int i = 0; i < candidates.size(); i++) {
			DualCube cell = candidates.get(i);
			cell.setInside(true);
		}

		while (candidates.size() > 0) {
			Vector<DualCube> nextCandidates = new Vector<DualCube>();
			for (int i = 0; i < candidates.size(); i++) {
				DualCube cell = candidates.get(i);
				/*
				 * int sX = max(xK(cell.getX()) - 1, 0); int sY =
				 * max(yK(cell.getY()) - 1, 0); int sZ = max(zK(cell.getZ()) -
				 * 1, 0);
				 * 
				 * int eX = min(xK(cell.getX()) + 2, nX); int eY =
				 * min(yK(cell.getY()) + 2, nY); int eZ = min(zK(cell.getZ()) +
				 * 2, nZ);
				 */

				int sX = max(cell.x - 1, 0);
				int sY = max(cell.y - 1, 0);
				int sZ = max(cell.z - 1, 0);

				int eX = min(cell.x + 2, nX);
				int eY = min(cell.y + 2, nY);
				int eZ = min(cell.z + 2, nZ);

				for (int x = sX; x < eX; x++) {
					for (int y = sY; y < eY; y++) {
						for (int z = sZ; z < eZ; z++) {
							DualCube nb = grid[x][y][z];
							if (!nb.isInside() && nb.getValue() > iso) {
								nb.setInside( true);
								nextCandidates.add(nb);
							}
						}
					}
				}
			}
			candidates = nextCandidates;
		}
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					DualCube cell = grid[x][y][z];
					if (!cell.isInside() && cell.getValue() > iso) {
						cell.setValue(0);//0
					}
				}
			}
		}
		if (true){
			for (int x = 0; x < nX; x++) {
				for (int y = 0; y < nY; y++) {
					for (int z = 0; z < nZ; z++) {
						DualCube cell = grid[x][y][z];
						if (!cell.isInside()
								&& (x == 0 || x == nX - 1 || z == 0
										|| z == nZ - 1 || y == 0 || y == nY - 1)) {
							// println("kleiner");
							cell.setValue(1000);
						}
					}
				}
			}
		}
	
		/*
		int maxOffset=8;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.inside) {
						if (x <=maxOffset  || y <= maxOffset || y >= nY -( maxOffset+1)
								|| z <= maxOffset || z >= nZ -( maxOffset+1)) {
							cell.setValue(0);
						}
					}
				}
			}
		}
		
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					Cell cell = grid[x][y][z];
					if (!cell.inside) {
						if (x == 0 || x == nX - 1 || y == 0 || y == nY - 1
								|| z == 0 || z == nZ - 1) {
							cell.setValue(1000);
						}
					}
				}
			}
		}*/
		// now find inner border= cell with lower iso
		// start to grow inside, cells with higher iso but inside
		// set the value iso + celldim
		// all cells inside with iso higher a certain range are outside again.

	}

	public void makeOffset() {
		float[][][] values = new float[nX][nY][nZ];
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					DualCube cell = grid[x][y][z];
					values[x][y][z] = cell.getValue();
					if (!cell.isInside()) {// nicht drin=hoher abstand
						values[x][y][z] = 10000;
					} else {
						if (cell.getValue() < iso + dim) {
							values[x][y][z] = -cell.getValue();// ist drin also
																// gleich 0
						} else {
							values[x][y][z] = -(iso + dim);
						}
					}
				}
			}
		}

		DepthAnalyser3D dA = new DepthAnalyser3D();
		dA.calculateDepth(values, dim * 1f * dimFac, dim * 1.414222f * dimFac,
				dim * 1.7321f * dimFac);
		
		values = dA.depth;
		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					DualCube cell = grid[x][y][z];
					if (!cell.isInside() && values[x][y][z] > iso) {
						cell.setValue(values[x][y][z]);
					} else {
						// cell.setValue(0);
					}
				}
			}
		}

		for (int x = 0; x < nX; x++) {
			for (int y = 0; y < nY; y++) {
				for (int z = 0; z < nZ; z++) {
					DualCube cell = grid[x][y][z];
					if (cell.getValue() < iso && x == 0 || x == nX - 1
							|| z == 0 || z == nZ - 1 || y == 0 || y == nY - 1) {
						// println("kleiner");
						cell.setValue(1000);
					}
				}
			}
		}
	}
	
	
	public float getX(int x){
		return sX+dim*x;
	}
	public float getY(int z){
		return sY+dim*z;
	}
	public float getZ(int z){
		return sZ+dim*z;
	}
	
	
	public int xK(float x) {
		return Math.round((x - sX) / dim);
	}

	public int yK(float y) {
		return Math.round((y - sY) / dim);
	}

	public int zK(float z) {
		return Math.round((z - sZ) / dim);
	}

	public void setBounds() {
		realBounds=mesh.getBounds3D();
		
		b3d.x1 = realBounds.x1 + minX;
		b3d.x2 = realBounds.x2 - maxX; // 0.05F
		b3d.z1 = realBounds.z1 + minZ ;// 0.5
		b3d.z2 = realBounds.z2 - maxZ ;// 0.5f
		b3d.y1 = realBounds.y1 + minY ;// 0.45
		b3d.y2 = realBounds.y2 - maxY ;
		
		
		//mesh.scale(scaleX, scaleY, scaleZ);
		//b3d=mesh.getBounds3D();
	}

	public void draw() {
	//	setBounds();
		if (flagResetPreview) {
			resetPreview();
			flagResetPreview = false;
		}
		if (flagResetFine) {
			resetFine();
			flagResetFine = false;
		}
		if (flagDistance) {
			createGrid();
			calculateDistances();
			
			flagDistance = false;
		}

		if (flagMarching) {
			dualCube();
			flagMarching = false;
		}
		
		if (flagSave){
			if (dualMesh!=null){
				dualMesh.saveObj(this.selectOutput());
				println("saved");
				flagSave=false;
			}
		}
		
		//if (flagRefresh) {
			println("draw");
			randomSeed(1);
		
			//camera(800,0,0,0,0,0,0,-1,0);
			lights();
			hint(ENABLE_DEPTH_TEST);
			pushMatrix();
			rectMode(CENTER);
			background(0);
			noStroke();
			fill(255);
			scale(200, 200, 200);
			
			
		
			float tol = 0.03f;
			noFill();
			strokeWeight(0.5f);
			stroke(0, 0, 255);
			realBounds.display3D(g);
			stroke(255, 0, 0);
			
			stroke(255, 0, 255);
			//fill(255);
			b3d.display3D(g);
		if (dc != null) {
			for (int x = 0; x < dc.nX - 1; x++) {
				for (int y = 0; y < dc.nY - 1; y++) {
					for (int z = 0; z < dc.nZ - 1; z++) {
						DualCube cube = dc.getCube(x, y, z);
						for (int i=0;i<cube.edges.length;i++){
							DualEdge edge=cube.edges[i];
							if (edge!=null&&edge.c1.isInside()!=edge.c2.isInside()){
								Vec3D c=edge.intersection;
								Vec3D n=edge.normal;
								Vec3D nMult=Vec3D.mult(n, 0.01f);
								line(c.x,c.y,c.z,c.x+nMult.x,c.y+nMult.y,c.z+nMult.z);
							}
						}
					}
				}
			}
		}
			if (!mousePressed){
				noStroke();
				if (meFaces != null) {
					for (int i = 0; i < meFaces.size(); i++) {
						SimpleTriangle f = meFaces.get(i);
						f.display3D(g);
					}
				}
				fill(255);
				if (dualMesh!=null){
					dualMesh.display3D(g);
				}
				fill(255,0,0);
				stroke(255,0,0);
				strokeWeight(2f);
				for (int i = 0; i < borderFaces.size(); i++) {
					SimpleTriangle tri = borderFaces.get(i);
					tri.display3D(g);
				}
			}
			
			popMatrix();
			hint(DISABLE_DEPTH_TEST);
		//}
		

		gui();
	}

	

	public void keyPressed() {
		if (key == 'd') {
			flagDistance = true;
			flagMarching = true;
		}
		if (key == 'f') {
			// flagMarching = true;
		}
		if (key == 'r') {
			flagResetPreview = true;
		}
		if (key == 't') {
			flagResetFine = true;
		}
		if (key == 's') {
			flagSave = true;
		}
		flagRefresh=true;

	}
}
